import React, { memo, useRef, useState, useEffect, useCallback, useImperativeHandle, forwardRef } from "react";
// antd
import {
  Form,
  Input,
  Button,
  InputNumber,
  Radio,
  Select,
  DatePicker,
  Row,
  Col,
} from "antd";
// propTypes
import PropTypes from "prop-types";
import "moment/locale/zh-cn";
import locale from "antd/es/date-picker/locale/zh_CN";

import global from "../../global/config";
import SelectComp from "../select";
import HYUploadComp from "../upload";
import HYEditor from "../editor";
import "./index.scss";

const { Option } = Select;

export interface commonOptions {
  label: string;
  value: boolean | string;
}
export interface nationOptions {
  label: string;
  value: string;
  num: string;
}
type Options = commonOptions[] | nationOptions[] | string;
export interface FormItem {
  type: string;
  label?: string;
  name?: string;
  required?: boolean;
  style?: object;
  options?: Options;
  rules?: any[];
  slotName?: string;
  className?: string;
  inline_formItem?: any;
  placeholder?: string;
  [propName: string]: any;
}
export interface FormLayout {
  labelCol?: { span: number };
  wrapperCol?: { span: number };
}
export interface InitialValues {
  [propName: string]: string | boolean | number;
}
interface MesPreix {
  [key: string]: string;
  Input: string;
  Radio: string;
  Select: string;
  Date: string;
  Upload: string;
  TextArea: string;
}
interface Props {
  formItem: FormItem[];
  // formItem: any;
  formLayout?: FormLayout;
  initialValues?: InitialValues;
  isShowBtn?: boolean;
  children?: React.ReactNode;
  submit?: any;
  isEdit?: boolean;
  [propName: string]: any;
}
const HYFormCom = forwardRef((props: Props, ref) => {
  useImperativeHandle(ref, () => ({
    onFinish: onFinish,
    formRef: formRef //外面拿到用于表单复制
  }));
  // data
  let { formLayout, isShowBtn, formItem, initialValues, isEdit } = props;
  const mesPreix: MesPreix = {
    Input: "请输入",
    Radio: "请选择",
    Select: "请选择",
    Date: "请选择",
    Upload: "请上传",
    TextArea: "请输入",
  };
  // Ref-hooks
  const formRef = useRef(null);
  // useState
  const [editData, setEditData] = useState({});

  // 初始化
  const initFormItem = () => {
    // 检验是否存在
    if (!formItem || (formItem && formItem.length === 0)) {
      return;
    }
    return formItem.map((item: FormItem) => createControl(item));
  };
  const createControl = (item: FormItem) => {
    let elem = null;
    switch (item.type) {
      case "Input":
        elem = inputElem(item);
        break;
      case "Select":
        elem = selectElem(item);
        break;
      case "InputNumber":
        elem = inputNumberElem(item);
        break;
      case "Radio":
        elem = radioElem(item);
        break;
      case "Slot":
        elem = slotElem(item);
        break;
      case "Column":
        elem = columnElem(item);
        break;
      case "TextArea":
        elem = textAreaElem(item);
        break;
      case "Date":
        elem = dateElem(item);
        break;
      case "formItemInline":
        elem = formItemInlineElem(item);
        break;
      //  这个根据外面传进来的url-从后端获取数据然后渲染 数据没有写死
      case "SelectComponent":
        elem = SelectComponent(item);
        break;
      case "Upload":
        elem = uploadElem(item);
        break;
      case "Editor":
        elem = editorElem(item);
        break;
      default:
        break;
    }
    return elem;
  };
  // 自带的校验--规则global[options]
  const rules = (data: FormItem) => {
    let rulesAry = [];
    const { required, message, rules, label, type } = data;
    if (required) {
      const msg = mesPreix[type] || "请选择";
      let messages = message || `${msg}${label}`;
      rulesAry.push({ required: true, message: messages });
    }
    if (rules && rules.length > 0) {
      rulesAry = [...rulesAry, ...rules];
    }
    return rulesAry;
  };
  const inputElem = (data: FormItem) => {
    const { label, name, style, placeholder } = data;
    const current_rules = rules(data);

    return (
      <Form.Item label={label} name={name} key={name} rules={current_rules}>
        <Input style={style} placeholder={placeholder} />
      </Form.Item>
    );
  };
  const selectElem = (data: FormItem) => {
    const { label, name, style, placeholder } = data;
    let options = data.options as commonOptions[] | string;
    const current_rules = rules(data);
    return (
      <Form.Item label={label} name={name} key={name} rules={current_rules}>
        <Select style={style} placeholder={placeholder}>
          {options === "status" &&
            global[options].map((elem, index) => {
              const { label, value } = elem;
              //下面value只能传入字符串 所以value为布尔就穿不进，拿到值还要处理我艹了-处理成布尔-这里根据实际项目需要改造
              return (
                <Option key={index} value={value.toString()}>
                  {label}
                </Option>
              );
            })}
          {Array.isArray(options) &&
            options.map((elem, index) => {
              const { label, value } = elem;
              return (
                <Option key={index} value={value.toString()}>
                  {label}
                </Option>
              );
            })}
        </Select>
      </Form.Item>
    );
  };
  const inputNumberElem = (data: FormItem) => {
    const { label, name, style, min, max, placeholder } = data;
    const current_rules = rules(data);
    return (
      <Form.Item label={label} name={name} key={name} rules={current_rules}>
        <InputNumber
          min={min}
          max={max}
          style={style}
          placeholder={placeholder}
        />
      </Form.Item>
    );
  };
  const radioElem = (data: FormItem) => {
    const { label, name, options, style } = data;
    const current_rules = rules(data);
    const option = options as commonOptions[];
    return (
      <Form.Item label={label} name={name} key={name} rules={current_rules}>
        <Radio.Group style={style}>
          {option.length > 0 &&
            option.map((elem, index) => {
              const { value, label } = elem;
              return (
                <Radio key={index} value={value}>
                  {label}
                </Radio>
              );
            })}
        </Radio.Group>
      </Form.Item>
    );
  };
  const slotElem = (data: FormItem) => {
    // ps 如果外面用到插槽（里面无法满足的情况） 那么不能通过直接set表单获取数据，重置数据，以及编辑渲染时手动将对于的数据渲染上，需要吧数据在外面单独定义，里面抛出一些数据手动的根据情况来处理(能做，但是会麻烦很多--万不得已建议少用)
    const { label, name, slotName } = data;
    const current_rules = rules(data);
    return (
      <Form.Item label={label} key={name} rules={current_rules}>
        {props.children && Array.isArray(props.children)
          ? props.children.filter(
            (elem: any) => elem.props.slot_name === slotName
          )
          : props.children}
      </Form.Item>
    );
  };
  const columnElem = (data: FormItem) => {
    const { label, className } = data;
    // 栏目名称
    return (
      <div className={className} key={label}>
        <h4>{label}</h4>
      </div>
    );
  };
  const textAreaElem = (data: FormItem) => {
    const { label, name, style, placeholder } = data;
    const current_rules = rules(data);
    return (
      <Form.Item label={label} name={name} key={name} rules={current_rules}>
        <Input.TextArea style={style} placeholder={placeholder} />
      </Form.Item>
    );
  };
  const dateElem = (data: FormItem) => {
    const { label, name, picker, style, format } = data;
    const current_rules = rules(data);
    return (
      <Form.Item label={label} name={name} key={name} rules={current_rules}>
        <DatePicker
          locale={locale}
          style={style}
          picker={picker}
          format={format}
        // 日期这里拿到的是标准国际日期 md--传的时候需要自己转一次
        ></DatePicker>
      </Form.Item>
    );
  };
  const formItemInlineElem = (data: any) => {
    // label 左侧名字 col_label左侧占的位置(栅格24) 和外面项一样的结构--是个数组inline_item
    const { label, col_label = 2, col_control = 22, inline_formItem } = data;
    const current_rules = rules(data);
    return (
      <Row key={label}>
        {/* 左侧名字--里面的部分样式需要重写-还需要在以前的基础上去拿 */}
        <Col
          span={col_label}
          className="ant-form-item"
          style={{ textAlign: "right" }}
        >
          <div className="ant-form-item-label">
            <label
              // for="name"
              className={
                current_rules.length > 0 && current_rules[0].required
                  ? "ant-form-item-required"
                  : ""
              }
            >
              {label}
            </label>
          </div>
        </Col>
        {/* 右侧：右侧又可以调用多个form组件 */}
        <Col span={col_control}>
          <Row>
            {inline_formItem &&
              inline_formItem.map((elem: FormItem) => {
                const { name, col } = elem;
                return (
                  <Col
                    className="form-item-inline-control"
                    key={name}
                    span={col}
                  >
                    {createControl(elem)}
                  </Col>
                );
              })}
          </Row>
        </Col>
      </Row>
    );
  };
  const SelectComponent = (data: FormItem) => {
    // 这里结合了SelectComponent一起封装的 --这里options和之前的options配置不一样
    const { label, name, url, style, selectOptions = [], propsKey = {} } = data;
    const current_rules = rules(data);
    return (
      // 要注意哪些参数是传给label 哪些是传给selectComp---后续考虑是不是有必要吧Form.Item封装到一起？  ---SelectComp这个可以通过onChange知道拿到val
      // 自定义规则没做 rules={...rules,...xx}
      <Form.Item label={label} name={name} rules={current_rules} key={name}>
        <SelectComp
          style={style}
          url={url}
          options={selectOptions}
          propsKey={propsKey}
        />
      </Form.Item>
    );
  };
  const uploadElem = (data: FormItem) => {
    const { label, name, style } = data;
    const current_rules = rules(data);
    return (
      <Form.Item label={label} name={name} key={name} rules={current_rules}>
        <HYUploadComp name={name} style={style}></HYUploadComp>
      </Form.Item>
    );
  };
  const editorElem = (data: FormItem) => {
    const { label, name, style } = data;
    const current_rules = rules(data);
    return (
      <Form.Item label={label} name={name} key={name} rules={current_rules}>
        <HYEditor name={name} style={style}></HYEditor>
      </Form.Item>
    );
  };
  // 生命周期
  useEffect(() => {
    if (JSON.stringify(props.editData) !== JSON.stringify(editData)) {
      setEditData(props.editData);
      if (Object.keys(props.editData).length > 0) {
        const formRefs = formRef.current as any;
        formRefs.setFieldsValue(props.editData);
      }
    }
  }, [props, editData]);

  const onFinish = useCallback(
    (values: any) => {
      props.submit(values, formRef.current); //外面拿到表单的值 和实例可以重置处理
    },
    [props]
  );
  return (
    <Form
      {...formLayout}
      initialValues={initialValues}
      onFinish={onFinish}
      ref={formRef}
    >
      {initFormItem()}
      <Form.Item>
        {isShowBtn ? (
          <Button htmlType="submit" type="primary">
            {isEdit ? "修改" : "添加"}
          </Button>
        ) : null}
      </Form.Item>
    </Form>
  );
});
HYFormCom.propTypes = {
  editData: PropTypes.object,
  formLayout: PropTypes.object,
  formItem: PropTypes.array,
  submit: PropTypes.func,
  initialValues: PropTypes.object,
  isShowBtn: PropTypes.bool,
};
// 默认
HYFormCom.defaultProps = {
  editData: {},
  formLayout: {},
  formItem: {},
  initialValues: {},
  isShowBtn: true,
};

export default memo(HYFormCom);
